/*
 * 描述： <描述>
 * 修改人： rain
 * 修改时间： 2016年8月18日
 * 项目： rainhy-auth
 */
package com.rainhy.auth.context;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.MultiValueMap;

import com.rainhy.auth.authchecker.SimpleAuthChecker;
import com.rainhy.auth.mock.MockAuthItemRef;
import com.rainhy.core.support.bean.Values;

/**
 * 权限容器<br>
 * XXX 以后可以根据需求提供 AuthItem 动态增加的功能,用来实现不同增加"产品"或者"地区"等类似数据时,自动增加权限项,这里就先不实现
 * 
 * @author rain
 * @version [版本号, 2016年8月18日]
 * @see [相关类/方法]
 * @since [产品/模块版本]
 */
public class AuthContext extends AuthContextBuilder implements InitializingBean {
    
    private static AuthContext context;
    
    /** 返回自身唯一引用 */
    public static AuthContext getContext() {
        return AuthContext.context;
    }
    
    /** 默认的权限检查器 */
    private final AuthChecker defaultAuthchecker = new SimpleAuthChecker();
    
    @Override
    public void afterPropertiesSet() throws Exception {
        AuthContext.context = this;
        super.afterPropertiesSet();
        
        logger.debug("[权限容器]启动完成...");
    }
    
    /**
     * 
     * 授权<br>
     * 系统会自动把相同 key 的权限用新权限引用把旧的替换掉
     *
     * @param authRefType 权限引用类型
     * @param authRefTypes 权限引用id
     * @param authItemKeys 权限keys
     * 
     * @return void [返回类型说明]
     * @throws [异常类型] [异常说明]
     * @see [类、类#方法、类#成员]
     * @version [版本号, 2016年10月3日]
     * @author rain
     */
    public void empower(String authRefType, String authRefId, String... authItemKeys) {
        for (String authItemKey : authItemKeys) {
            AuthItem authItem = getAuthItemByKey(authItemKey);
            
            MockAuthItemRef mockAuthItemRef = new MockAuthItemRef(authItem, authRefType, authRefId);
            mockAuthItemRef.setEffectiveDate(null);
            mockAuthItemRef.setInvalidDate(null);
            
            this.authManager.persist(mockAuthItemRef);
        }
    }
    
    /**
     * 返回所有的权限集合(key:权限key)<br>
     *
     * @return Map<String,AuthItem> [返回类型说明]
     * @throws [异常类型] [异常说明]
     * @see [类、类#方法、类#成员]
     * @version [版本号, 2016年9月29日]
     * @author rain
     */
    public Map<String, AuthItem> getAuthItemMapping() {
        return this.authItemMapping;
    }
    
    /**
     * 
     * 鉴权<br>
     * 1.权限 key 不存在返回 true<br>
     * 2.如果对应权限项已经无效，则认为拥有权限<br>
     * 3.如果当前权限引用依赖有效时间，则判断该权限引用是否还有效
     * 
     *
     * @param authKey 权限项id
     * @param authRefId 权限引用
     * 
     * @return boolean [返回类型说明]
     * @exception throws [异常类型] [异常说明]
     * @see [类、类#方法、类#成员]
     * @version [版本号, 2016年8月23日]
     * @author rain
     */
    public boolean hasAuth(String authKey, Object... objects) {
        if (!authItemMapping.containsKey(authKey)) {
            return true;
        }
        
        //检查对应权限的权限类型是否正确
        AuthItem authItem = authItemMapping.get(authKey);
        
        //如果对应权限项已经无效，则认为拥有权限
        if (!authItem.isValid()) {
            return true;
        }
        
        //如果当前权限引用依赖有效时间，则判断该权限引用是否还有效
        List<AuthItemRef> authItemRefs = AuthSessionContext.getAuthRefsFromSession(authItem.getKey());
        if (CollectionUtils.isNotEmpty(authItemRefs)) {
            if (MapUtils.isNotEmpty(authCheckerMapping)) {
                String authType = authItem.getAuthType();
                List<AuthChecker> authCheckers = null;
                if (authType != null) {
                    // 根据权限类型获取对应的权限检查器映射
                    authCheckers = this.authCheckerMapping.get(authType);
                    if (CollectionUtils.isNotEmpty(authCheckers)) {
                        for (AuthChecker ac : authCheckers) {
                            if (ac.hasAuth(authItemRefs, objects)) {
                                return true;
                            }
                        }
                    }
                }
                
                // null能够鉴权所有的权限
                authCheckers = this.authCheckerMapping.get(null);
                if (CollectionUtils.isNotEmpty(authCheckers)) {
                    for (AuthChecker ac : authCheckers) {
                        if (ac.hasAuth(authItemRefs, objects)) {
                            return true;
                        }
                    }
                }
            } else {
                return this.defaultAuthchecker.hasAuth(authItemRefs, objects);
            }
        }
        return false;
    }
    
    /**
     * 操作员登录<br>
     * 登录时,获取到该
     *
     * @param refType2RefIdMapping 权限引用类型和权限引用id的映射
     * 
     * @return void [返回类型说明]
     * @throws [异常类型] [异常说明]
     * @see [类、类#方法、类#成员]
     * @version [版本号, 2016年9月29日]
     * @author rain
     */
    public void login(MultiValueMap<String, String> refType2RefIdMapping) {
        List<AuthItemRef> authItemRefList = new ArrayList<AuthItemRef>();
        if (MapUtils.isEmpty(refType2RefIdMapping)) {
            AuthSessionContext.putAuthRefsToSession(authItemRefList);
            return;
        }
        
        Values<String> superAdminInfo = getSuperAdminInfo(refType2RefIdMapping);
        if (superAdminInfo != null) {   //如果是超级管理员则拥有所有权限项的引用
            for (AuthItem authItem : getAuthItems()) {
                //构建的超级管理员的权限引用
                authItemRefList.add(new MockAuthItemRef(authItem, superAdminInfo.getValue1(), superAdminInfo.getValue2()));
            }
        } else {
            // 如果不是超级管理员，根据引用表查询得到相关的权限引用
            Collection<AuthItemRef> authItemRefs = this.authManager.queryAuthItemRef(refType2RefIdMapping);
            if (CollectionUtils.isNotEmpty(authItemRefs)) {
                for (AuthItemRef authItemRefTemp : authItemRefs) {
                    loadAuthItemRef(authItemRefTemp);
                }
            }
            authItemRefList.addAll(authItemRefs);
        }
        
        //将权限引用写入容器
        AuthSessionContext.putAuthRefsToSession(authItemRefList);
    }
    
    /**
     * 
     * 返回超级管理员信息<br>
     * 如果返回 null, 则不是超级管理员
     *
     * @param refType2RefIdMapping
     * 
     * @return Values<String> [返回类型说明]
     * @throws [异常类型] [异常说明]
     * @see [类、类#方法、类#成员]
     * @version [版本号, 2016年9月30日]
     * @author rain
     */
    private Values<String> getSuperAdminInfo(MultiValueMap<String, String> refType2RefIdMapping) {
        if (CollectionUtils.isNotEmpty(this.adminCheckers)) {
            for (Map.Entry<String, List<String>> refType2RefIdEntry : refType2RefIdMapping.entrySet()) {
                for (AdminChecker adminChecker : adminCheckers) {
                    String authRefType = refType2RefIdEntry.getKey();
                    List<String> authRefIds = refType2RefIdEntry.getValue();
                    if (CollectionUtils.isNotEmpty(authRefIds)) {
                        for (String refId : authRefIds) {
                            if (adminChecker.isSuperAdmin(authRefType, refId)) {
                                return new Values<>(authRefType, refId);
                            }
                        }
                    }
                }
            }
        }
        return null;
    }
    
    /**
     * 给权限引用加载权限项
     * 
     * @param authItemRef 权限引用
     * 
     * @return void [返回类型说明]
     * @exception throws [异常类型] [异常说明]
     * @see [类、类#方法、类#成员]
     */
    private void loadAuthItemRef(AuthItemRef authItemRef) {
        if (authItemRef == null || authItemRef.getAuthItem() == null) {
            return;
        }
        
        AuthItem realAuthItem = authItemMapping.get(authItemRef.getAuthItem().getKey());
        if (realAuthItem != null) {
            if (authItemRef instanceof MockAuthItemRef) {
                ((MockAuthItemRef) authItemRef).setAuthItem(realAuthItem);
            }
        }
    }
}
